#ifndef RENDERER_MESH_DATA_HEAP_H
#define RENDERER_MESH_DATA_HEAP_H




#define USE_MESH_DATA_HEAP 0


#if USE_MESH_DATA_HEAP

class CRendererMeshDataHeap
{
public:
	enum
	{
		HeapAddressSpaceSize = 32 * 1024 * 1024,
		DefaultVirtualAllocThreshold = 64 * 1024,
	};

public:
	CRendererMeshDataHeap()
		: m_virtualAllocThreshold(DefaultVirtualAllocThreshold)
	{
		m_heap = HeapCreate(HEAP_NO_SERIALIZE, 64 * 1024, HeapAddressSpaceSize);

		CryGetIMemReplay()->RegisterFixedAddressRange(reinterpret_cast<void*>(m_heap), HeapAddressSpaceSize, "Mesh Data Heap");
	}

	~CRendererMeshDataHeap()
	{
		HeapDestroy(m_heap);
	}

	void SetVirtualAllocThreshold(size_t threshold)
	{
		m_virtualAllocThreshold = threshold;
	}

	void* Allocate(size_t sz, size_t align)
	{
		CryAutoLock<CryCriticalSectionNonRecursive> lock(m_lock);

		void* ptr = NULL;
		bool isVirtual = false;

		if (sz >= m_virtualAllocThreshold)
		{
			ptr = VirtualAlloc(NULL, sz, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
			isVirtual = true;
		}
		else
		{
			ptr = HeapAlloc(m_heap, HEAP_NO_SERIALIZE, sz + align);
		}

		if (ptr)
		{
			void* retPtr = align 
				? reinterpret_cast<void*>((reinterpret_cast<INT_PTR>(ptr) + (align - 1)) & ~(align - 1))
				: ptr;

			{
				MEMREPLAY_SCOPE(EMemReplayAllocClass::C_UserPointer, EMemReplayUserPointerClass::C_CryMalloc);
				
				uint heapSize = 0;
				
				heapSize = isVirtual
					? (sz + 4095) & ~4095
					: HeapSize(m_heap, HEAP_NO_SERIALIZE, ptr);

				MEMREPLAY_SCOPE_ALLOC(ptr, heapSize, 0);
			}
			
			m_ptrs.insert(std::make_pair(retPtr, AllocInfo(ptr, isVirtual)));

			return retPtr;
		}

		return NULL;
	}

	bool TryFree(void* ptr)
	{
		CryAutoLock<CryCriticalSectionNonRecursive> lock(m_lock);

		std::map<void*, AllocInfo>::iterator it = m_ptrs.find(ptr);
		if (it != m_ptrs.end())
		{
			{
				MEMREPLAY_SCOPE(EMemReplayAllocClass::C_UserPointer, EMemReplayUserPointerClass::C_CryMalloc);
				
				if (it->second.IsVirtual())
					VirtualFree(it->second.Ptr(), 0, MEM_RELEASE);
				else
					HeapFree(m_heap, HEAP_NO_SERIALIZE, it->second.Ptr());
					
				MEMREPLAY_SCOPE_FREE(ptr);
			}
			
			m_ptrs.erase(it);
			return true;
		}

		return false;
	}

private:
	struct AllocInfo
	{
		UINT_PTR i;

		AllocInfo(void* ptr, bool isVirtual)
			: i(reinterpret_cast<UINT_PTR>(ptr) | (isVirtual ? 1 : 0))
		{
		}

		void* Ptr() { return reinterpret_cast<void*>(i & ~1); }
		bool IsVirtual() { return (i & 1) != 0; }
	};

private:
	size_t m_virtualAllocThreshold;

	CryCriticalSectionNonRecursive m_lock;
	HANDLE m_heap;
	std::map<void*, AllocInfo> m_ptrs;
};

#endif

#endif
